package sk.stuba.fiit.clientApp.service.impl;

import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3._2000._09.xmldsig.ReferenceType;
import org.w3._2000._09.xmldsig.SignatureType;

import sk.stuba.fiit.clientApp.service.TimeStampService;
import sk.stuba.fiit.clientApp.util.Util;
import sk.stuba.fiit.clientApp.ws.client.SignatureServiceClient;
import sk.stuba.fiit.pki.ws.schema.beans.TimestampRequest;
import sk.stuba.fiit.pki.ws.schema.beans.TimestampResponse;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

@Service("timeStampService")
public class TimeStampServiceImpl implements TimeStampService{

	@Autowired
	private SignatureServiceClient signatureServiceClient;
	
	@Override
	public TimeStampResponse obtainInitializationTimeStamp(SignatureType signatureType, ASN1ObjectIdentifier algorithmOid, String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, TSPException {
		List<ReferenceType> reference = signatureType.getSignedInfo().getReference();
		int size = 0;
		for(int i=0; i < reference.size(); i++){
			size += reference.get(i).getDigestValue().length;
		}
		byte[] data = new byte[size];
		int index=0;
		for(int i=0; i < reference.size(); i++){
			byte[]tmp = reference.get(i).getDigestValue();
			for(int j = 0; j<tmp.length; j++){
				data[index++]=tmp[j];
			}
		}
		return getTimeStampToken(data, algorithmOid, algorithm);
	}
	@Override
	public TimeStampResponse obtainAllDataTimeStamp(SignatureType signatureType, ASN1ObjectIdentifier algorithmOid, String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, TSPException {
		List<ReferenceType> reference = signatureType.getSignedInfo().getReference();
		int size = 0;
		for(int i=0; i < reference.size(); i++){
			if(reference.get(i).getType() == null || !reference.get(i).getType().equals(Util.SIGNED_PROPERTIES_TYPE)){
				size += reference.get(i).getDigestValue().length;
			}
		}
		byte[] data = new byte[size];
		int index=0;
		for(int i=0; i < reference.size(); i++){
			if(reference.get(i).getType() == null || !reference.get(i).getType().equals(Util.SIGNED_PROPERTIES_TYPE)){
				byte[]tmp = reference.get(i).getDigestValue();
				for(int j = 0; j<tmp.length; j++){
					data[index++]=tmp[j];
				}
			}
		}
		return getTimeStampToken(data, algorithmOid, algorithm);
	}

	@Override
	public TimeStampResponse obtainSignatureTimestamp(SignatureType signatureType, ASN1ObjectIdentifier algorithmOid, String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, TSPException {
		byte[] data = signatureType.getSignatureValue().getValue();
		return getTimeStampToken(data, algorithmOid, algorithm);
	}
	
	@Override
   public TimeStampResponse getTimeStampToken(byte[] data, ASN1ObjectIdentifier algorithmOid, String algorithm) throws NoSuchAlgorithmException, NoSuchProviderException, IOException, TSPException {
		
		TimeStampRequestGenerator generator = new TimeStampRequestGenerator();
		ASN1ObjectIdentifier asn1objectidentifier = algorithmOid;		
		BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());		
		MessageDigest hash = MessageDigest.getInstance(algorithm, "BC");
		hash.update(data);		
		TimeStampRequest timestamprequest = generator.generate(asn1objectidentifier, hash.digest(), nonce);		
		
		BASE64Encoder encoder = new BASE64Encoder();
		Collection<String> reqString = new ArrayList<String>(3);
		reqString.add(encoder.encodeBuffer(timestamprequest.getEncoded()));				
		TimestampRequest tsreq = new TimestampRequest();		
		tsreq.withTimestamreq(reqString);
		TimestampResponse response = signatureServiceClient.getTimeStamp(tsreq);
		
		List<String> resToken = response.getTimestamresp();
		BASE64Decoder decoder = new BASE64Decoder();
		TimeStampResponse result = new TimeStampResponse(decoder.decodeBuffer(resToken.get(0)));
		result.validate(timestamprequest);
		return result;
	}

}
